﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using Microsoft.Xna.Framework;

namespace Babylon
{
    public static class Utilities
    {
        public static float AspectRatio(this Size size)
        {
            return (float) (size.Width/size.Height);
        }
        public static Vector2 ReadVector2(this BinaryReader reader)
        {
            return new Vector2(reader.ReadSingle(), reader.ReadSingle());
        }

        public static Vector3 ReadVector3(this BinaryReader reader)
        {
            return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
        }

        public static Matrix ReadMatrix(this BinaryReader reader)
        {
            return new Matrix
                (
                    reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(),
                    reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(),
                    reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(),
                    reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()
                );
        }

        public static Color ReadColor(this BinaryReader reader)
        {
            return new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
        }

        public static Guid ReadGuid(this BinaryReader reader)
        {
            return Guid.Parse(reader.ReadString());
        }

        public static Color Multiply(this Color c1, Color c2)
        {
            float r = (c1.R / 255.0f) * (c2.R / 255.0f);
            float g = (c1.G / 255.0f) * (c2.G / 255.0f);
            float b = (c1.B / 255.0f) * (c2.B / 255.0f);
            float a = (c1.A / 255.0f) * (c2.A / 255.0f);

            return new Color(r, g, b, a);
        }

        public static Vector4 ToVector4(this Color color)
        {
            return new Vector4((float)(color.R / 255.0f),
                (float)(color.G / 255.0f),
                (float)(color.B / 255.0f),
                (float)(color.A / 255.0f));
        }

        public static float DotCoordinate(this Plane plane, Vector3 point)
        {
            return ((((plane.Normal.X * point.X) + (plane.Normal.Y * point.Y)) + (plane.Normal.Z * point.Z)) + plane.D);
        }


        public static Vector4[] ToVector4Array(this Matrix matrix)
        {
            Vector4[] result = new Vector4[4];

            result[0] = new Vector4(matrix.M11, matrix.M12, matrix.M13, matrix.M14);
            result[1] = new Vector4(matrix.M21, matrix.M22, matrix.M23, matrix.M24);
            result[2] = new Vector4(matrix.M31, matrix.M32, matrix.M33, matrix.M34);
            result[3] = new Vector4(matrix.M41, matrix.M42, matrix.M43, matrix.M44);

            return result;
        }

        public static ushort[] ToUShortArray(this List<int> list)
        {
            return list.Select(i => (ushort) i).ToArray();
        }

        public static Vector3 Unproject(Vector3 source, float viewportWidth, float viewportHeight, Matrix world, Matrix view, Matrix projection)
        {
            Matrix matrix = Matrix.Multiply(world, view);
            matrix = Matrix.Multiply(matrix, projection);
            matrix = Matrix.Invert(matrix);
            source.X = source.X / viewportWidth * 2f - 1f;
            source.Y = -(source.Y / viewportHeight * 2f - 1f);
            Vector3 vector = Vector3.Transform(source, matrix);
            float num = source.X * matrix.M14 + source.Y * matrix.M24 + source.Z * matrix.M34 + matrix.M44;
            
            if (!WithinEpsilon(num, 1f))
            {
                vector /= num;
            }
            
            return vector;
        }

        public static Ray CreateRay(float x, float y, float viewportWidth, float viewportHeight, Matrix world, Matrix view, Matrix projection)
         {
             Vector3 start = Unproject(new Vector3(x, y, 0), viewportWidth, viewportHeight, world, view, projection);
             Vector3 end = Unproject(new Vector3(x, y, 1), viewportWidth, viewportHeight, world, view, projection);
             
             Vector3 direction = end - start;
             direction.Normalize();
             
             return new Ray(start, direction);
         }

        public static bool WithinEpsilon(float a, float b)
        {
            float num = a - b;
            return -1.401298E-45f <= num && num <= 1.401298E-45f;
        }
    }
}
